# coding: utf8

# TODO: ОБЯЗАТЕЛЬНО: List of Int

import os
import subprocess
from coding_console_fix import setup_console
from ala_classes import SyntaxTreeNode
from ala_classes import Token, TokenType as T
from ala_classes import AlaCodeGeneratorException, AlaNotImplementedException, AlaFunctionException
from asm_template import *

PRINT_TYPE_DIRECTIVE = {
    '<INT>': 'format_int',
    '<STRING>': 'format_str',
    '<BOOL>': 'format_int',
    '<ARRAY_OF_INT>': 'format_int'
}

OP_PRIORITY = {
    'VAL_INT': 0,
    'ID': 0,
    've_plus_minus': 1,
    've_mult_div': 2,
    've_unary_plus_minus': 3,
    've_index': 3,
    've_brackets': 3
}


class AsmBuilder():

    def __init__(self, g_symbol_table):
        self.asm_body_code = [r'']
        self.code_block_stack = []
        self.src_dest = self.asm_body_code
        self.vars = r''

        self.str_counter = 0
        self.int_counter = 0

        self.g_symbol_table = g_symbol_table
        self.global_var_list = []

    def push_code_block(self):
        self.code_block_stack.append([r''])
        self.src_dest = self.code_block_stack[-1]

    def write_to_dest(self, src):
        self.src_dest[0] += src

    def get_current_src(self):
        return self.src_dest[0]

    def pop_code_block(self):
        if len(self.code_block_stack) == 1:
            self.src_dest = self.asm_body_code

        if self.code_block_stack:
            src = self.code_block_stack.pop()[0]
        else:
            src = self.asm_body_code[0]

        if len(self.code_block_stack) >= 1:
            self.src_dest = self.code_block_stack[-1]

        return src

    def get_src(self):
        asm_code_file = START_CODE.format(os.path.join(os.path.abspath('./fasm/'), 'include/'), self.vars)
        asm_code_file += self.asm_body_code[0]
        asm_code_file += END_CODE
        return asm_code_file

    def push_stack_for_calc(self, op1, op2):
        if op1:
            if op1 in self.global_var_list:
                op1 = self.__dereference(op1)
            self.write_to_dest(PUSH.format(op1))
        if op2:
            if op2 in self.global_var_list:
                op2 = self.__dereference(op2)
            self.write_to_dest(PUSH.format(op2))

    def add(self, op1, op2):
        self.push_stack_for_calc(op1, op2)
        self.write_to_dest(ADD)

    def sub(self, op1, op2, left_priority):
        if not op1 and op2:
            self.write_to_dest(SUB.format(push_op2=PUSH.format(op2), op1_reg='eax', op2_reg='edx'))
        elif op1 and not op2:
            self.push_stack_for_calc(op1, op2)
            self.write_to_dest(SUB.format(push_op2='', op1_reg='eax', op2_reg='edx'))
        elif op1 and op2:
            self.push_stack_for_calc(op1, op2)
            self.write_to_dest(SUB.format(push_op2='', op1_reg='edx', op2_reg='eax'))
        else:
            if left_priority:
                self.write_to_dest(SUB.format(push_op2='', op1_reg='edx', op2_reg='eax'))
            else:
                self.write_to_dest(SUB.format(push_op2='', op1_reg='eax', op2_reg='edx'))

    def get_array_elem_by_index(self, arr_name, index, calc_node_or_token=None):
        if type(index) == int:
            offset = '+' + str(index * 4) if index != 0 else ''
            calc_offset = ''
        else:
            offset = '+' + index
            self.push_code_block()
            if type(calc_node_or_token) == Token:
                self.push(self.__dereference(calc_node_or_token.value))
                self.pop('eax')
            else:
                self.calc_expr(calc_node_or_token.production, calc_node_or_token.parts)
            calc_offset = self.pop_code_block()
        self.write_to_dest(GET_ARRAY_ELEM_BY_INDEX.format(arr_name=arr_name, offset=offset,
                                                          calc_offset=calc_offset))

    def unary_minus(self, op):
        self.push_stack_for_calc(op, None)
        self.write_to_dest(UNARY_MINUS)

    def mul(self, op1, op2):
        self.push_stack_for_calc(op1, op2)
        self.write_to_dest(MUL)

    def div(self, op1, op2, left_priority):
        if not op1 and op2:
            self.write_to_dest(DIV.format(push_op2=PUSH.format(op2), op1_reg='eax', op2_reg='ebx'))
        elif op1 and not op2:
            self.push_stack_for_calc(op1, op2)
            self.write_to_dest(DIV.format(push_op2='', op1_reg='eax', op2_reg='ebx'))
        elif op1 and op2:
            self.push_stack_for_calc(op1, op2)
            self.write_to_dest(DIV.format(push_op2='', op1_reg='ebx', op2_reg='eax'))
        else:
            if left_priority:
                self.write_to_dest(DIV.format(push_op2='', op1_reg='ebx', op2_reg='eax'))
            else:
                self.write_to_dest(DIV.format(push_op2='', op1_reg='eax', op2_reg='ebx'))

    def pop(self, dest):
        self.write_to_dest(POP.format(dest))

    def push(self, arg):
        self.write_to_dest(PUSH.format(arg))

    def calc_expr(self, node_production, expr):
        if node_production in ['ve_plus_minus', 've_mult_div']:
            op1 = None if type(expr[0]) == SyntaxTreeNode else expr[0].value
            op2 = None if type(expr[2]) == SyntaxTreeNode else expr[2].value
            if node_production == 've_plus_minus':
                if Token.type_string(expr[1]) == 'PLUS':
                    self.add(op1, op2)
                else:
                    left_priority = False
                    if OP_PRIORITY[expr[0].get_type()] >= OP_PRIORITY[expr[2].get_type()]:
                        left_priority = True
                    self.sub(op1, op2, left_priority)

            elif node_production == 've_mult_div':
                if Token.type_string(expr[1]) == 'MULT':
                    self.mul(op1, op2)
                else:
                    left_priority = False
                    if OP_PRIORITY[expr[0].get_type()] >= OP_PRIORITY[expr[2].get_type()]:
                        left_priority = True
                    self.div(op1, op2, left_priority)

        elif node_production == 've_unary_plus_minus':
            op = None if type(expr[0]) == SyntaxTreeNode else expr[1].value
            if Token.type_string(expr[0]) == 'MINUS':
                self.unary_minus(op)

        elif node_production == 've_index':
            arr_name = expr[0].value
            if type(expr[1]) == Token and type(expr[1].value) == int:
                index = int(expr[1].value)
                self.get_array_elem_by_index(arr_name, index)
            else:
                index = 'edx'
                self.get_array_elem_by_index(arr_name, index, expr[1])

    def __get_logic_operand_type(self, op):
        if type(op) != SyntaxTreeNode:
            if op.type_string() == 'ID' or op.type_string() == 'VAL_INT':
                op_type = op.value
            elif op.type == T.TRUE:
                op_type = 'True'
            elif op.type == T.FALSE:
                op_type = 'False'
            else:
                raise AlaCodeGeneratorException("Type Error in logic expr!")
        else:
            op_type = 'eax'

        return op_type

    def compare_equal(self, op1, op2):
        self.push_stack_for_calc(op1, op2)
        self.write_to_dest(EQUAL)

    def compare_not_equal(self, op1, op2):
        self.push_stack_for_calc(op1, op2)
        self.write_to_dest(NOT_EQUAL)

    def compare_less(self, op1, op2):
        if not op1 and op2:
            self.write_to_dest(LESS.format(PUSH.format(op2)))
        else:
            self.push_stack_for_calc(op2, op1)
            self.write_to_dest(LESS.format(''))

    def compare_less_eq(self, op1, op2):
        if not op1 and op2:
            self.write_to_dest(LESS_EQ.format(PUSH.format(op2)))
        else:
            self.push_stack_for_calc(op2, op1)
            self.write_to_dest(LESS_EQ.format(''))

    def compare_more(self, op1, op2):
        if not op1 and op2:
            self.write_to_dest(MORE.format(PUSH.format(op2)))
        else:
            self.push_stack_for_calc(op2, op1)
            self.write_to_dest(MORE.format(''))

    def compare_more_eq(self, op1, op2):
        if not op1 and op2:
            self.write_to_dest(MORE_EQ.format(PUSH.format(op2)))
        else:
            self.push_stack_for_calc(op2, op1)
            self.write_to_dest(MORE_EQ.format(''))

    def logic_and(self, op1, op2):
        self.push_stack_for_calc(op1, op2)
        self.write_to_dest(LOGIC_AND)

    def logic_or(self, op1, op2):
        self.push_stack_for_calc(op1, op2)
        self.write_to_dest(LOGIC_OR)

    def logic_not(self):
        self.write_to_dest(LOGIC_NOT)

    def logic_expr(self, node_production, expr):
        if len(expr) > 2:
            op1 = self.__get_logic_operand_type(expr[0])
            op2 = self.__get_logic_operand_type(expr[2])
            if Token.type_string(expr[1]) == 'EQUAL':
                self.compare_equal(op1, op2)
            elif Token.type_string(expr[1]) == 'NOT_EQUAL':
                self.compare_not_equal(op1, op2)
            elif Token.type_string(expr[1]) == 'LESS':
                self.compare_less(op1, op2)
            elif Token.type_string(expr[1]) == 'MORE':
                self.compare_more(op1, op2)
            elif Token.type_string(expr[1]) == 'LESS_E':
                self.compare_less_eq(op1, op2)
            elif Token.type_string(expr[1]) == 'MORE_E':
                self.compare_more_eq(op1, op2)
            elif Token.type_string(expr[1]) == 'AND':
                self.logic_and(op1, op2)
            elif Token.type_string(expr[1]) == 'OR':
                self.logic_or(op1, op2)

        elif len(expr) == 2:
            if Token.type_string(expr[0]) == 'NOT':
                if type(expr[1]) != SyntaxTreeNode:
                    self.write_to_dest(PUSH.format(self.__dereference(expr[1].value)))
                self.logic_not()

        elif len(expr) == 1:
            if Token.type_string(expr[0]) in ['TRUE', 'FALSE', 'ID']:
                if Token.type_string(expr[0]) == 'TRUE':
                    self.write_to_dest(PUSH.format(1))
                elif Token.type_string(expr[0]) == 'FALSE':
                    self.write_to_dest(PUSH.format(0))
                else:
                    self.write_to_dest(PUSH.format(self.__dereference(expr[0].value)))
        else:
            pass

    def add_glob_var(self, var_type, name, value='?'):
        if name not in self.global_var_list:
            if var_type == "INT":
                self.vars += VAR_INT.format(name, value)
            elif var_type == "STRING":
                self.vars += VAR_STRING.format(name, value)
            elif var_type == "BOOL":
                self.vars += VAR_BOOL.format(name, value)
            elif var_type == "ARRAY_OF_INT":
                self.vars += VAR_LIST_INT.format(name, value)
            self.global_var_list.append(name)

    def __dereference(self, arg):
        if not (type(arg) == str or type(arg) == unicode):
            raise TypeError('Var name must be string. ' + str(type(arg)) + ' given.')

        if arg[0] != '[':
            arg = '[' + arg + ']'
        return arg

    def __is_dereferenced(self, arg):
        if arg[0] != '[':
            return True
        return False

    def assign_expr(self, var, value, calc_list=list()):

        if type(var) == SyntaxTreeNode:
            if var.production == 've_index':
                arr_name = var.parts[0].value
                self.write_to_dest(INIT_STACK_ARRAY.format(arr_name=arr_name))
                offset = ''
                if var.parts[1].value:
                    offset = '+' + str(int(var.parts[1].value) * 4)
                self.write_to_dest(ASSIGN_ARRAY_VAR.format(assign_from=value, offset=offset))

        elif value == 'eax':
            var = self.__dereference(var)
            self.write_to_dest(ASSIGN.format(var, value))

        elif value == 'bool':
            self.write_to_dest(ASSIGN_BOOL.format(var))

        elif value in ['True', 'False']:
            var = self.__dereference(var)
            self.write_to_dest(ASSIGN.format(var, value))

        elif type(value) == list:
            init_array = ''
            self.write_to_dest(INIT_STACK_ARRAY.format(arr_name=var))
            for i, arr_elem in enumerate(value):
                offset = ''
                if i != 0:
                    offset = '+' + str(i * 4)
                if type(arr_elem) == SyntaxTreeNode:
                    self.write_to_dest(calc_list.pop(0))
                    self.write_to_dest(ASSIGN_ARRAY_INT.format(offset=offset, assign_from='eax'))
                elif arr_elem.type == T.VAL_INT:
                    assign_from = arr_elem.value
                    init_array += ASSIGN_ARRAY_INT.format(offset=offset, assign_from=assign_from)
                else:
                    assign_from = self.__dereference(arr_elem.value)
                    init_array += ASSIGN_ARRAY_VAR.format(offset=offset, assign_from=assign_from)
            self.write_to_dest(init_array)

        elif type(value) == Token:
            var = self.__dereference(var)
            if value.type == T.ID:
                token_value = self.__dereference(value.value)
            else:
                token_value = int(value.value)

            self.write_to_dest(ASSIGN_NOT_CALC.format(var, token_value))

    def __get_var_name_and_type(self, item):
        print_type = ''
        value = ''
        if item.type in [T.TRUE, T.FALSE]:
            print_type = 'format_int'
            value = 'True' if item.type == T.TRUE else T.FALSE
        elif item.type_string() == "VAL_STR":
            print_type = 'format_str'
            value = 'str' + str(self.str_counter)
            self.add_glob_var("STRING", value, item.value)
            self.str_counter += 1
        elif item.type_string() == "VAL_INT":
            print_type = "format_int"
            value = item.value
        elif item.type_string() == "ID":
            print_type = PRINT_TYPE_DIRECTIVE[str(self.g_symbol_table[str(item.value)])]
            value = self.__dereference(str(item.value)) if print_type != 'format_str' else str(item.value)
        return print_type, value

    def print_expr(self, expr, calc_list=list()):
        if len(expr):
            if type(expr[0]) == SyntaxTreeNode and expr[0].production in ['ve_plus_minus', 've_mult_div',
                                                                          've_unary_plus_minus', 've_index',
                                                                          've_compare', 've_and', 've_or', 've_not']:
                self.write_to_dest(calc_list.pop(0))
                self.write_to_dest(POP.format('eax'))
                self.write_to_dest(PRINT.format('format_int', 'eax'))
                self.write_to_dest(PRINTLN)

            elif type(expr[0]) == SyntaxTreeNode and expr[0].production == 've_list_print':
                is_not_println = type(expr[0].parts[-1]) == Token and expr[0].parts[-1].type == T.COMMA
                if is_not_println:
                    expr[0].parts.pop()
                for item in expr[0].parts:
                    if type(item) == SyntaxTreeNode:
                        if item.production in ['ve_plus_minus', 've_mult_div', 've_index', 've_unary_plus_minus']:
                            self.write_to_dest(calc_list.pop(0))
                            self.write_to_dest(POP.format('eax'))
                            self.write_to_dest(PRINT.format('format_int', 'eax'))
                    else:
                        print_type, value = self.__get_var_name_and_type(item)
                        self.write_to_dest(PRINT.format(print_type, value))
                if not is_not_println:
                    self.write_to_dest(PRINTLN)

            else:
                print_type, value = self.__get_var_name_and_type(expr[0])
                self.write_to_dest(PRINT.format(print_type, value))
                self.write_to_dest(PRINTLN)
        else:
            self.write_to_dest(PRINTLN)

    def if_expr(self, else_label, after_if_label, then_code, else_code):
        if else_code:
            self.write_to_dest(IF_ELSE.format(else_label=else_label, after_if_label=after_if_label, then_code=then_code, else_code=else_code))
        else:
            self.write_to_dest(IF.format(then_code=then_code, after_if_label=after_if_label))

    def while_expr(self, loop_label, condition, body, end_label):
        self.write_to_dest(WHILE.format(loop_label=loop_label, end_label=end_label, condition=condition, body=body))

    def __value_or_name_checker(self, arg):
        if arg in ['eax', 'ebx', 'ecx', 'edx']:
            return arg

        try:
            arg = int(arg)
        except:
            arg = self.__dereference(arg)

        return arg

    def for_expr(self, var, loop_label, exit_label, body, arg1, arg2, arg3, calc_arg1, calc_arg2, calc_arg3):
        self.add_glob_var('INT', var)

        arg2 = self.__value_or_name_checker(arg2)
        arg3 = self.__value_or_name_checker(arg3)
        var = self.__dereference(var)

        self.push_code_block()
        if arg1 == 'eax':
            self.pop('eax')
        self.assign_expr(var, arg1)
        assign_counter = self.pop_code_block()

        set_end = PUSH.format('ebx') if calc_arg2 else ''
        set_step = PUSH.format('edx') if calc_arg3 else ''
        get_end = POP.format('ebx') if calc_arg2 else ''
        get_step = POP.format('edx') if calc_arg3 else ''
        self.write_to_dest(FOR_IN.format(var=var,
                                         loop_label=loop_label, exit_label=exit_label,
                                         body=body,
                                         end=arg2, step=arg3,
                                         calc_start=calc_arg1, calc_end=calc_arg2, calc_step=calc_arg3,
                                         assign_counter=assign_counter,
                                         set_end=set_end, set_step=set_step, get_end=get_end, get_step=get_step))

    def input_func(self, var_name, val_type, input_arg, input_arg_counter):
        if input_arg:
            const_str_name = 'input_arg_' + str(input_arg_counter)
            self.add_glob_var('STRING', const_str_name, input_arg)
            self.write_to_dest(PRINT.format('format_str', const_str_name))

        if val_type == 'INT':
            self.write_to_dest(INPUT.format(var_name))
        else:
            self.write_to_dest(RAW_INPUT.format(var_name))


class CodeGenerator():

    def __init__(self, src_file_name, tree, g_symbol_table):
        self.src_filename = self.__get_filename_without_extension(src_file_name)
        self.tree = tree
        self.g_symbol_table = g_symbol_table
        self.builder = AsmBuilder(g_symbol_table)

        self.asm_compiler = os.path.join(os.path.abspath('./fasm'), 'FASM.EXE')
        self.asm_src_filename = os.path.join(os.path.abspath('./'), self.src_filename + '.asm')
        self.result_exe_filename = os.path.join(os.path.abspath('./'), self.src_filename + '.exe')

        self.while_counter = 0
        self.if_counter = 0
        self.for_counter = 0
        self.input_arg_counter = 0

    def __get_filename_without_extension(self, filename):
        if len(filename.split('.')[:-1]) > 0:
            filename = '.'.join(filename.split('.')[:-1])
        return filename

    def __save_code_to_file(self):
        with open(self.asm_src_filename, 'w') as asm_src:
            asm_src.write(self.builder.get_src())

    def __call_asm_compiler(self):
        setup_console()
        asm = subprocess.Popen(' '.join([self.asm_compiler, self.asm_src_filename, self.result_exe_filename]),
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
        output, err = asm.communicate()
        if err:
            #self.__delete_src_asm_file()
            raise AlaCodeGeneratorException(u'\nError Text:\n' + err.decode("cp866"))

    def __delete_src_asm_file(self):
        if os.path.exists(self.asm_src_filename) and os.path.isfile(self.asm_src_filename):
            os.remove(self.asm_src_filename)

    def __node_recognition(self, node):
        for child in node.parts:
            if type(child) == SyntaxTreeNode and \
                    child.production != 'start' and \
                    node.production != 'while_expr' and \
                    node.production != 'for_expr' and \
                    node.production != 'print_expr':
                self.__node_recognition(child)

        if node.production in ['ve_plus_minus', 've_mult_div', 've_unary_plus_minus', 've_index']:
            print 'calc_expr'
            self.builder.calc_expr(node.production, node.parts)

        if node.production in ['ve_compare', 've_and', 've_or', 've_not']:
            print 'logic_expr'
            self.builder.logic_expr(node.production, node.parts)

        elif node.production == 'assign_expr':
            print 'assign_expr'

            arr_calc_list = []
            init_value = '?'
            value = 'eax'

            if type(node.parts[0]) == SyntaxTreeNode and node.parts[0].production == 've_index':
                value = 'eax' if type(node.parts[2]) == SyntaxTreeNode else node.parts[2].value
                self.builder.assign_expr(node.parts[0], value, arr_calc_list)
                return

            var_type = self.g_symbol_table[node.parts[0].value].type_string()
            if var_type not in ['UNKNOWN', 'ARRAY_OF_INT']:
                if var_type == 'STRING' and len(node.parts[2].parts[1].parts):
                    init_value = node.parts[2].value
                self.builder.add_glob_var(var_type, node.parts[0].value, init_value)

            if var_type == 'BOOL':
                if type(node.parts[2]) != SyntaxTreeNode:
                    if node.parts[2].type == T.FALSE:
                        value = 'False'
                    elif node.parts[2].type == T.TRUE:
                        value = 'True'
                else:
                    value = 'bool'

            elif type(node.parts[2]) == SyntaxTreeNode and node.parts[2].production == 've_funcall_id_or_value' and str(node.parts[2].parts[0].value) in ['input', 'raw_input']:
                input_value_type = 'STRING' if str(node.parts[2].parts[0].value) == 'raw_input' else 'INT'
                self.builder.add_glob_var(input_value_type, node.parts[0].value)
                input_arg = node.parts[2].parts[1].parts[0].value if len(node.parts[2].parts[1].parts) else ''
                self.builder.input_func(node.parts[0].value, input_value_type, input_arg, self.input_arg_counter)
                self.input_arg_counter += 1
                return

            elif var_type == 'ARRAY_OF_INT':
                if type(node.parts[2].parts[0]) == SyntaxTreeNode:
                    arr_len = str(len(node.parts[2].parts[0].parts))
                    self.builder.add_glob_var(var_type, node.parts[0].value, arr_len)
                else:
                    self.builder.add_glob_var(var_type, node.parts[0].value, '1')

                if type(node.parts[2].parts[0]) == Token:
                    value = node.parts[2].parts
                else:
                    value = node.parts[2].parts[0].parts

                for item in value:
                    if type(item) == SyntaxTreeNode:
                        self.builder.push_code_block()
                        self.__node_recognition(item)
                        arr_calc_list.append(self.builder.pop_code_block())

            elif type(node.parts[2]) == Token:
                value = node.parts[2]

            if init_value == '?':
                self.builder.assign_expr(node.parts[0].value, value, arr_calc_list)

        elif node.production == 'print_expr':
            print 'print_expr'
            calc_list = []
            if type(node.parts[0]) == SyntaxTreeNode:
                print_list = node.parts
                if node.parts[0].production == 've_list_print':
                    print_list = node.parts[0].parts

                for item in print_list:
                    if type(item) == SyntaxTreeNode:
                        self.builder.push_code_block()
                        self.__node_recognition(item)
                        calc_list.append(self.builder.pop_code_block())

            self.builder.print_expr(node.parts, calc_list)

        elif node.production == 'if_expr':
            print 'if_expr'
            else_label = 'else_label_' + str(self.if_counter)
            after_if_label = 'after_if_label_' + str(self.if_counter)
            self.if_counter += 1

            self.builder.push_code_block()
            self.__node_recognition(node.parts[1])
            then_code = self.builder.pop_code_block()

            else_code = None
            if len(node.parts) > 2:
                self.builder.push_code_block()
                self.__node_recognition(node.parts[2])
                else_code = self.builder.pop_code_block()

            if type(node.parts[0]) != SyntaxTreeNode:
                self.builder.logic_expr('', [node.parts[0]])

            self.builder.if_expr(else_label, after_if_label, then_code, else_code)

        elif node.production == 'while_expr':
            print 'while_expr'
            begin_label = 'while_' + str(self.while_counter) + '_begin'
            end_label = 'while_' + str(self.while_counter) + '_end'
            self.while_counter += 1

            self.builder.push_code_block()
            if type(node.parts[0]) != SyntaxTreeNode:
                self.builder.logic_expr('', [node.parts[0]])
            else:
                self.__node_recognition(node.parts[0])
            while_condition = self.builder.pop_code_block()

            self.builder.push_code_block()
            self.__node_recognition(node.parts[1])
            do_code = self.builder.pop_code_block()
            self.builder.while_expr(begin_label, while_condition, do_code, end_label)

        elif node.production == 'for_expr':
            print 'for_expr'

            for_loop_label = 'for_loop_label_' + str(self.for_counter)
            for_exit_label = 'for_exit_label_' + str(self.for_counter)
            self.for_counter += 1

            calc_arg1, calc_arg2, calc_arg3 = '', '', ''
            arg1, arg2, arg3 = 0, None, 1
            if node.parts[1].production == 've_funcall_id_or_value':
                if node.parts[1].parts[0].value == 'range':
                    if 0 < len(node.parts[1].parts[1].parts) < 4:
                        if type(node.parts[1].parts[1].parts[0]) == SyntaxTreeNode:
                            range_args_len = len(node.parts[1].parts[1].parts[0].parts)
                            arg1 = node.parts[1].parts[1].parts[0].parts[0]
                            if type(arg1) != SyntaxTreeNode:
                                arg1 = arg1.value
                            else:
                                self.builder.push_code_block()
                                self.builder.calc_expr(arg1.production, arg1.parts)
                                calc_arg1 = self.builder.pop_code_block()
                                arg1 = 'eax'

                            arg2 = node.parts[1].parts[1].parts[0].parts[1]
                            if type(arg2) != SyntaxTreeNode:
                                arg2 = arg2.value
                            else:
                                self.builder.push_code_block()
                                self.builder.calc_expr(arg2.production, arg2.parts)
                                calc_arg2 = self.builder.pop_code_block()
                                arg2 = 'ebx'

                            if range_args_len > 2:
                                arg3 = node.parts[1].parts[1].parts[0].parts[2]
                                if type(arg3) != SyntaxTreeNode:
                                    arg3 = arg3.value
                                    if int(arg3) < 1:
                                        raise AlaFunctionException('Шаг не может быть меньше 1')
                                    elif int(arg3) > 2147483648:
                                        raise AlaFunctionException('Слишком большой шаг, возможно переполнение.')
                                else:
                                    self.builder.push_code_block()
                                    self.builder.calc_expr(arg3.production, arg3.parts)
                                    calc_arg3 = self.builder.pop_code_block()
                                    arg3 = 'edx'
                        else:
                            arg2 = node.parts[1].parts[1].parts[0].value
                    else:
                        raise AlaFunctionException('В функции range должен быть минимум 1 и максимум 3 аргумента.')
                else:
                    raise AlaNotImplementedException('Для инструкции for реализована только функция range([start,] finish [,step]).')

            self.builder.push_code_block()
            self.__node_recognition(node.parts[2])
            do_code = self.builder.pop_code_block()

            for_var_name = node.parts[0].value

            self.builder.for_expr(for_var_name,
                                  for_loop_label, for_exit_label,
                                  do_code,
                                  arg1, arg2, arg3,
                                  calc_arg1, calc_arg2, calc_arg3)

    def __tree_walk(self):
        if self.tree.parts:
            self.__node_recognition(self.tree.parts[0])

    def compile(self):
        self.__tree_walk()
        self.__save_code_to_file()
        self.__call_asm_compiler()
        #self.__delete_src_asm_file()